﻿namespace Baggr.Agent
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Threading;
    using Wintellect.Threading.AsyncProgModel;

    public sealed class FeedUpdateTask
    {
        private LoadFeed _LoadFeed;
        private readonly string _ConnectionString;
        private bool _CanContinue = true;
        private readonly AsyncEnumerator _AsyncEnumerator = new AsyncEnumerator();
        private ManualResetEvent _Pause = new ManualResetEvent(true);
        GetFeedsForUpdate _GetFeedForUpdateCommand;
        private int _FeedsInProcess = 0;
        private readonly PublishFeed _PublishFeedCommand;

        public bool Paused
        {
            set
            {
                if (!_CanContinue)
                    return;

                if (value)
                    _Pause.Set();
                else
                    _Pause.Reset();
            }
        }
        private int MaxProcessingFeeds
        {
            get
            {
                return Agent.Default.MaxProcessingFeeds;
            }
        }
        private int MinProgresingFeeds
        {
            get
            {
                return Agent.Default.MinProcessingFeed;
            }
        }

        public FeedUpdateTask(string connectionString)
        {
            if (string.IsNullOrEmpty(connectionString))
                throw new ArgumentNullException("connectionString");
            _ConnectionString = connectionString;
            _GetFeedForUpdateCommand = new GetFeedsForUpdate(connectionString);
            _PublishFeedCommand = new PublishFeed(connectionString);
            _LoadFeed = new LoadFeed(connectionString);
        }
        public IAsyncResult BeginExecute(AsyncCallback callback, object state)
        {
            return _AsyncEnumerator.BeginExecute(ExecutingProcess(), callback, state);
        }

        public void EndExecute(IAsyncResult result)
        {
            _AsyncEnumerator.EndExecute(result);
        }

        private IEnumerator<int> ExecutingProcess()
        {
            while(_CanContinue)
            {
                _Pause.WaitOne();
                Debug.Assert(MaxProcessingFeeds > _FeedsInProcess);
                var ae2 = new AsyncEnumerator<Dictionary<Guid, string>>();
                ae2.BeginExecute(
                    _GetFeedForUpdateCommand.BeginExecute(MaxProcessingFeeds - _FeedsInProcess, ae2),
                    result => OnNewFeedForUpdate(ae2.EndExecute(result), _AsyncEnumerator.End()),
                    ae2);
                _FeedsInProcess = MaxProcessingFeeds;
                yield return 1;
                if (_FeedsInProcess > MinProgresingFeeds)
                    yield return _FeedsInProcess - MinProgresingFeeds;
            }
        }
        private void OnNewFeedForUpdate(
           Dictionary<Guid, string> feeds,
            AsyncCallback callback)
        {
            foreach (var feed in feeds)
            {
                _LoadFeed.BeginLoad(
                    feed.Key,
                    feed.Value,
                    loadedResult => { _FeedsInProcess--; callback(loadedResult); });
            }
        }
        public void Break()
        {
            _CanContinue = false;
            _Pause.Reset();
        }
    }


}
